//
//  JoNetTask.swift
//  iOS Example
//
//  Created by tong on 16/1/4.
//  Copyright © 2016年 Alamofire. All rights reserved.
//

import UIKit
import Foundation
import Alamofire
import JoLoading
import Eelay
import RxSwift

public class NetStatus:NSObject{
    static var manager:NetworkReachabilityManager? = nil
    public var hasNetwork = true
    public static let share = NetStatus()
    let bag = DisposeBag()
    public override init() {
        super.init()
       start()
    }
    public func start() {
        let obj: Observable<NetworkReachabilityManager.NetworkReachabilityStatus> = Observable.create {[weak self] observer -> Disposable in
                NetStatus.manager = NetworkReachabilityManager(host: "www.apple.com")
                NetStatus.manager?.startListening{ status in
                    switch status {
                    case .notReachable:
                        self?.hasNetwork = false
                    default:
                        self?.hasNetwork = true
                    }
                    observer.onNext(status)
                }
                return Disposables.create()
            }
        self.status = obj
        status?.subscribeOn(MainScheduler.instance).subscribe(onNext:{
            f in
        }).disposed(by: bag)
    }
    
    public var status:Observable<NetworkReachabilityManager.NetworkReachabilityStatus>? = nil
}

public enum ResponseStatus{
    case success
    case error
    case cancel
    case unAuth
    public var isSuccess:Bool{
        get{
            switch self {
            case .success:
                return true
            default:
                return false
            }
        }
    }
}

//public typealias JoDataResponse = DataResponse<Any,JoError>
public protocol GlobalResponseHandle{
    var handle:JoTaskHandle{get}
}


public struct JoResponse{
    public typealias json_response = (_ status:ResponseStatus,_ message:String,_ obj:RootData?,_ response:Any?,_ task:JoTask?) ->Void
    public typealias text_response = (_ status:ResponseStatus,_ message:String,_ obj:RootData?,_ response:Any?,_ task:JoTask?) ->Void
    public typealias data_response = (_ status:ResponseStatus,_ message:String,_ obj:RootData?,_ response:Any?,_ task:JoTask?) ->Void
}

typealias JoDataResponse = Any
public typealias RootData = Any


public struct ApijsonTask {
    public var request:Dictionary<String,Any>
    public var response:Any?
    
    public var hasRootList:(Bool,String){
        get{
            let keys = request.keys
            let list = keys.filter { (str) -> Bool in
                 return str.hasSuffix("[]")
            }
            if let first = list.first{
                return (true,first)
            }
            return (false,"")
        }
    }
    
    public init(request:Dictionary<String,Any>,response:Any?) {
        self.request = request
        self.response = response
    }
    //MARK:为Paginator自动分页添加分页参数
    public mutating func appendPageParams(paginator:JoPage,page:Int,count:Int) ->Dictionary<String,Any>{
        let node = paginator.node ?? "[]"
        if var list = request[node] as? Dictionary<String,Any>
        {
            list["page"] = page
            list["count"] = count
            request[node] = list
        }
        return request
        
        
    }
    
    //MARK:APIJSON全局处理返回结果
    public var handle:(ResponseStatus,message:String,obj:RootData){
        get{
            if let dict = response as? NSDictionary
            {
                let mu_dict = dict.mutable_dictionary
                if mu_dict["code",""].hasPrefix("20")
                {
                    if mu_dict.allKeys.count < 3{
                        let list_option = hasRootList
                        if list_option.0{
                            mu_dict["\(list_option.1)"] = NSMutableArray()
                            return (.success,mu_dict["msg",""],mu_dict)
                        }
                        else{
                            return (.success,mu_dict["msg",""],mu_dict)
                        }
                    }
                    else{
                        return (.success,mu_dict["msg",""],mu_dict)
                    }
                }
                else{
                    return (.error,mu_dict["msg",""],mu_dict)
                }
            }
            return (.error,"can'not get a valid root json api struct",response ?? NSMutableDictionary())
        }
    }
    
}


public enum ApiMode{
    case normal    //常规接口
    case apijson(Dictionary<String,Any>)  //APIJSON.cn           //http://apijson.cn，https://github.com/TommyLemon/APIJSON/blob/master/Document.md#1
    case module(String)
    
}





func getNodeObj<T>(root:AnyObject?,node:String,handle:(T)->Void)
{
    if let dict = root as? NSMutableDictionary
    {
        if let obj = dict[obj:node,nil] as? T
        {
            handle(obj)
        }
    }
}

open class JoRequest:NSObject
{
    
    public var apimode:ApiMode = .normal
    
    public final var url_value = ""
    public final var method_value: HTTPMethod = .get
    public final var params_value = [String:Any]()
    public final var params_encode_value:ParameterEncoding = URLEncoding.queryString
    public var headers_value:[String:String]?
    public final var request: DataRequest?
    
    

//    public static var scheme_default:Analyse = .Json(data: "data", message: "msg", status: "status")
//
//    public var scheme_value:Analyse?//最外层返回状态的判断解析方案
//
//    public var ansys_scheme:Analyse{
//        if let v = scheme_value{
//            return v
//        }
//        return JoRequest.scheme_default
//    }
//
    
    static var success_tags = ["1","success","succeed","ok"]

    
    public var list_enum_block:((NSMutableDictionary)->Void)?
    
    final var json_handle_block:JoResponse.json_response?
    final var text_handle_block:JoResponse.text_response?
    var test_json_data:Any? = nil

    final var if_log_request:Bool = false
    
    final var if_log_response:Bool = false
    
    final var test_delay_time:Double = 1

    
    final var auto_run = false
    
    public final var files = [TaskFile]()
    
    public static var headerCreator:((JoTask) -> [String:String])?//全局请求头的包装
    public static var global_header:[String:String] = [String:String]()
    
    public static var paramsCreator:((JoTask) -> [String:Any])?//全局请求参数的包装
    public static var global_params:[String:Any] = [String:Any]()


//    open func getHeaders() ->[String:String]
//    {
//
//        var bheaders = [String:String]()
//        if let h_b = JoRequest.headerCreator
//        {
//            bheaders = h_b(nil)
//        }
//
//        if let hs = self.headers_value
//        {
//            return hs + JoTask.global_header + bheaders
//        }
//
//
//        return JoTask.global_header + bheaders
//    }
    
//    open func getParams() ->[String:Any]
//    {
//
//        var bpramas = [String:Any]()
//        if let h_b = JoRequest.paramsCreator
//        {
//            bpramas = h_b()
//        }
//
//        bpramas = params_value + bpramas + JoTask.global_params
//        return bpramas
//    }
    

    deinit
    {
        self.request?.cancel()
    }
    
    
    
}



public typealias JoTaskHandle = (_:JoTask?,_ respnse:Any?,_ url:String?, _  mode:ApiMode?)->(status:ResponseStatus,message:String,obj:RootData)


public typealias JoTaskModeModuleHandle = (_ mode:ApiMode,_ url:String?,_ params:[String:Any]?,_ data:Data?,_ response:Any,_ task:JoTask?,_  block:@escaping JoResponse.json_response) ->Void



open class JoTask:JoRequest
{
    public var tag:String? = nil//全局参数某些情况下需要特殊过滤是可以用tag 区别
    @discardableResult func tag(_ tag:String?)->JoTask{
        self.tag = tag
        return self
    }
    public static var moduleModeHandle:JoTaskModeModuleHandle = { _,_,_,_,_,_,_ in
        
    }
    public var group_header:[String:String] = [:]
    
    public var resultTip:LoadResultType? = nil

    
    public var server:JoTaskServerImplement? = nil
    open func getHeaders() ->[String:String]
    {
        
        var bheaders = [String:String]()
        if let h_b = JoRequest.headerCreator
        {
            bheaders = h_b(self)
        }
        
        if let hs = self.headers_value
        {
            return JoTask.global_header + group_header + bheaders + hs
        }
        
        
        return JoTask.global_header + group_header  + bheaders
    }
    
    public var group_params:[String:Any] = [:]
    open func getParams() ->[String:Any]
    {
        
        var bpramas = JoRequest.paramsCreator?(self) ?? [:]
        bpramas =  params_value + group_params + bpramas +  JoTask.global_params
        return bpramas
    }

    
    public static var willRun:(JoTask)->Void = {_ in}
    
    public static var taskID:String? = nil
    
    public var encode_param_to_url = false
    public var fullQurey:JoTask{
        encode_param_to_url = true
        return self
    }
    public static var defaultHandle:JoTaskHandle = {
        task,result,url,mode in
        
        switch (mode ?? .normal){
        case .apijson(let apijson):
            let jsonmode = ApijsonTask(request: apijson, response: result)

            return jsonmode.handle
        default:
            if let dict = result as? NSDictionary{
                let mu_dict = dict.mutable_dictionary
                if mu_dict["status",""] == "1"
                {
                    let data = mu_dict["data"]
                    return (.success,mu_dict["message",""],data as RootData)
                }
                if mu_dict["status",""] == "0"
                {
                    let data = mu_dict["data"]
                    return (.error,mu_dict["message",""],data as RootData)
                }
            }
            return (.error,"can'not get a valid root json",NSMutableDictionary())
        }
    }
    
    public var dataHandle:JoTaskHandle = JoTask.defaultHandle{
        didSet{
//            print("====KKKKKoo----",dataHandle)
        }
    }
    
    public static var debugLog = true
    
    public static var tasks = [JoTask]()
    
    public var status:JoTaskModel = .prepare
    
    class func clear(){
        JoTask.tasks = JoTask.tasks.filter{$0.__valid}
    }
    func __drop() {
        self.status = .prepare
        self.__valid = false
    }
    
    
    var uuid:String = UUID().uuidString
    var __requestID:String? = nil

    var __valid = true
    

    public override init() {
        super.init()
        JoTask.tasks.append(self)
    }
    
    
    
    
    @discardableResult
    public func requestID(_ ID:String) -> JoTask {
        self.__requestID = ID
        return self
    }
    
    @discardableResult
    public func append(file:TaskFile) -> JoTask {
        self.files.append(file)
        return self
    }
    @discardableResult
    public func append(headers:[String:String]) -> JoTask {
        if self.headers_value != nil
        {
            
        }else{
            self.headers_value = [String:String]()
        }
        
        self.headers_value = self.headers_value! + headers
        return self
    }
    @discardableResult
    public func handle(_ handleRoot:@escaping JoTaskHandle) -> JoTask {
        self.dataHandle = handleRoot
        return self
    }
    
    @discardableResult
    public func test_json(_ data:Any?) -> JoTask {
        self.test_json_data = data
        return self
    }
    
    @discardableResult
    public func test_delay(_ delaySecond:Double) -> JoTask {
        self.test_delay_time = delaySecond
        return self
    }
    
    
    
    
}

extension JoTask {
    
    public static func isCancel(response:Any)->Bool{
        if "\(response)".contains("Code=-999"){
            return true
        }
        return false
    }
    
    public var param_query_encoding:JoTask{
        self.params_encode_value = URLEncoding.queryString
        return self
    }
    
    public var param_body_encoding:JoTask{
        self.params_encode_value = URLEncoding.httpBody
        return self
    }
    
    public var json_body_encoding:JoTask{
        headers_value = ["Content-Type":"application/json"]
        self.params_encode_value = JSONEncoding.default
        return self
    }
    
    
    public var param_url_encoding:JoTask{
        self.params_encode_value = URLEncoding.queryString
        return self
    }
    public var post:JoTask{
        self.method_value = .post
        self.params_encode_value = URLEncoding.httpBody
        return self
    }
    
    public var get:JoTask{
        self.method_value = .get
        self.params_encode_value = URLEncoding.queryString
        
        return self
    }
    
    public var log:JoTask{
        self.if_log_request = true
        return self
    }
    
    
    public var log_nil:JoTask{
        self.if_log_request = false
        return self
    }
    
    @discardableResult
    public func json_handle(_ handle:@escaping JoResponse.json_response) ->JoTask{
        self.json_handle_block = handle
        return self
    }
    
    
    @discardableResult
    public func log_response() ->JoTask
    {
        self.if_log_response = true
        return self
        
    }
    
    @discardableResult
    public func text_handle(_ handle:@escaping JoResponse.text_response) ->JoTask
    {
        self.text_handle_block = handle
        return self
    }
    
    @discardableResult
    public func params(_ params:[String:Any]) ->JoTask
    {
        self.params_value = params
        return self
    }
    @discardableResult
    public func apijson(_ params:[String:Any]) ->JoTask
    {
        self.apimode = .apijson(params)
        self.params_encode_value = JSONEncoding.default
        self.params_value = params
        return self
    }
    @discardableResult
    public func custom(_ server:JoTaskServerImplement) ->JoTask
    {
        self.server = server
        return self
    }

    
    @discardableResult
    public class func post(url:String)->JoTask
    {
        let task = JoTask()
        task.url_value = url
        task.method_value = .post
        
        return task
    }
    
    @discardableResult
    public class func get(url:String)->JoTask
    {
        let task = JoTask()
        task.url_value = url
        task.method_value = .get
        return task
        
    }
    
    
    @discardableResult
    public func cancel() ->JoTask
    {
        if let r = self.request
        {
            r.cancel()
        }
        return self
    }
    @discardableResult
    public func url(_ url:String)->JoTask
    {
        self.url_value = url
        return self
    }
    
}


public enum JoTaskModel{
    case prepare
    case working
}



extension JoTask
{
    
    public var qurey:String?{
        var querys = [URLQueryItem]()

        let params = getParams()
        for (key,v) in params {
            querys.append(URLQueryItem(name: key, value: "\(v)"))
        }
            
        if let cps = NSURLComponents(string: ""){
            cps.queryItems = querys
            return cps.url?.query
        }
        return nil

    }
    
    public func encode_query(urlStr:String,params:[String:Any])->String{
        guard var url = URLComponents(string: urlStr) else {
            return urlStr
        }
//        if params.count < 1{
//            return self.urlString
//        }
//
        var querys = [URLQueryItem]()
        if let ps = url.queryItems{
            querys = ps
        }
        for (key,v) in params {
            querys.append(URLQueryItem(name: key, value: "\(v)"))
        }
            
        
        url.queryItems = querys
        return url.url?.absoluteString ?? self.url_value
    }
    
    
            
    
    
    @discardableResult
    public func run() ->JoTask
    {
        if let _ = server{
            runCustomizeServer()
            return self
        }
        if let json_data = test_json_data as? NSJsonObj{
            if let json:NSMutableDictionary = json_data.jsonObj(){
//                print("------TEST -- ",json)
                simulate(json, time: self.test_delay_time)
                return self
            }
            if let json:NSMutableArray = json_data.jsonObj(){
//                print("------TEST -- ",json)
                simulate(json, time: self.test_delay_time)
                return self
            }
        }
        
        JoTask.clear()
        
        
        self.status = .working
        JoTask.willRun(self);
        
        
    
        var params:[String:Any] = getParams()
        var url_ = url_value
        if self.encode_param_to_url{
            url_ = encode_query(urlStr: url_value, params: params)
            params = [:]
        }
        if JoTask.debugLog
        {
            if self.if_log_request
            {
                print("JoTask:\(method_value)|\(url_)\nparameters:\(params)\nheaders:\(self.getHeaders())")
            }
        }
        
        self.request = AF.request(url_, method: self.method_value, parameters: params, encoding: params_encode_value, headers: HTTPHeaders(getHeaders()), interceptor: nil, requestModifier: nil)
      
      

//        self.request = Alamofire.request(url_value,method: self.method_value, parameters: params, encoding: params_encode_value, headers: self.getHeaders())
        
        if let json_b = self.json_handle_block
        {
            weak var wself = self
            self.request?.responseJSON(completionHandler: { response in
                if JoTask.debugLog
                {
                    if wself?.if_log_response ?? false
                    {
                        print("JoTask request:\(String(describing: wself?.method_value))|\(String(describing: url_))\nparameters:\(String(describing: wself?.getParams()))\nheaders:\(String(describing: wself?.self.getHeaders()))")
                        print("JoTask response :\n code \(String(describing: response.response?.statusCode))  \(response)")
                    }
                }
                if let info = wself?.dataHandle(wself,response,wself?.url_value,wself?.apimode)
                {
                    json_b(info.status,info.message,info.obj,response,wself)
                    wself?.__drop()
                    return
                }
                
//                if response.error == nil
//                {
//                    let mode = wself?.apimode ?? .normal
//                    switch mode{
//                    case .module:
//                        JoTask.moduleModeHandle(mode,wself?.url_value,params,response.data,response,wself,json_b)
//                            wself?.__drop()
//                        return
//                    default:
//                        
//                        if let info = wself?.dataHandle(wself,response,wself?.url_value,wself?.apimode)
//                        {
//                            json_b(info.status,info.message,info.obj,response,wself)
//                            wself?.__drop()
//                            return
//                        }
//                    }
//                }
//                else{
//                    
//                    if JoTask.isCancel(response: response){
//                        json_b(.cancel,"已取消数据请求".loc,NSMutableDictionary(), response,wself)
//                        return
//                    }
//                    let code = response.response?.statusCode ?? 0
//                    let codeStr = "\(code)"
//                    if codeStr.starts(with: "5"){
//                        json_b(.error,"数据服务错误(\(code))",NSMutableDictionary(), response,wself)
//                        return
//                    }
//                    if codeStr.starts(with: "40"){
//                        json_b(.error,"数据访问失败(\(code))",NSMutableDictionary(), response,wself)
//                        return
//                    }
//                }
//                
//                json_b(.error,"获取数据失败(未知错误)！",response.value as AnyObject?,response,wself)
                wself?.__drop()
                return
                
            })
        }
        else if self.text_handle_block != nil
        {
            self.request?.responseString(completionHandler: { (str) in
          
//                text_b(.success,"ok",str.value as AnyObject?,str)
            })
            __drop()
            
        }
        
        return self
    }
    
    
    
    //mark 测试模拟
    public func simulate(_ result:RootData?,time:Double=0.3){
        weak var wself = self
        JoTask.clear()
        if JoTask.debugLog
        {
            if self.if_log_request
            {
                print("JoTask:\(method_value)|\(url_value)\nparameters:\(getParams())\nheaders:\(self.getHeaders())")
            }
        }
        if let json_b = self.json_handle_block{
            if let r = result
            {
                
                
                co_delay(delay: time) {
                    let resp = DataResponse(request: nil, response: nil, data: nil, metrics: nil, serializationDuration: 1, result: Result<Any,JoError>.success("模拟数据成功".loc))
                    
//                    let resp = DataResponse(request: nil,response:nil,data: nil,result: Result<Any>.success("模拟数据成功".loc))
                    json_b(.success,"数据请求成功[模拟]".loc,r,resp,wself)
                }
               
            }
            else{
                co_delay(delay: time) {
//                    let resp = DataResponse(request: nil,response:nil,data: nil,result: Result<Any>.success("模拟数据失败".loc))
                    let resp = DataResponse(request: nil, response: nil, data: nil, metrics: nil, serializationDuration: 1, result: Result<Any,JoError>.success("模拟数据成功".loc))

                    json_b(.success,"模拟数据失败[模拟]".loc,result,resp,wself)
                }
            }
            wself?.__drop()
        }
    }
    
    
    
}

extension JoTask{
    public func runCustomizeServer() {
        weak var wself = self
        JoTask.clear()

        if JoTask.debugLog
        {
            if self.if_log_request
            {
                print("JoTask:\(method_value)|\(url_value)\nparameters:\(getParams())\nheaders:\(self.getHeaders())")
            }
        }
        guard let json_b = self.json_handle_block else {
            return
        }
        guard let server_ = server else {
            return
        }
        server_.start(task: self) { (status, data, msg) in
            let resp = DataResponse(request: nil, response: nil, data: nil, metrics: nil, serializationDuration: 1, result: Result<Any,JoError>.success(msg))
            json_b(status,msg,data,resp,wself)
            wself?.__drop()
        }
    }
}

public struct JoError:Error{
    let message:String
    init(err:String) {
        message = err
    }
}






